/* 
Copyright Paul James Mutton, 2001-2004, http://www.jibble.org/

This file is part of SimpleFTP.

This software is dual-licensed, allowing you to choose between the GNU
General Public License (GPL) and the www.jibble.org Commercial License.
Since the GPL may be too restrictive for use in a proprietary application,
a commercial license is also provided. Full license information can be
found at http://www.jibble.org/licenses/

$Author: pjm2 $
$Id: SimpleFTP.java,v 1.2 2004/05/29 19:27:37 pjm2 Exp $

*/

package simpleftp.client.control;

import java.io.*;
import java.net.*;
import java.util.*;


/**
 * SimpleFTP is a simple package that implements a Java FTP client.
 * With SimpleFTP, you can connect to an FTP server and upload multiple files.
 *  <p>
 * Copyright Paul Mutton,
 *           <a href="http://www.jibble.org/">http://www.jibble.org/</a>
 * 
 */
public class SimpleFTP {

	/**
     * Create an instance of SimpleFTP.
     */
    public SimpleFTP() {
        
    }
    
    
    /**
     * Connects to the default port of an FTP server and logs in as
     * anonymous/anonymous.
     */
    public void connect(String host) throws IOException {
        connect(host, 21);
    }
    
    
    /**
     * Connects to an FTP server and logs in as anonymous/anonymous.
     */
    public void connect(String host, int port) throws IOException {
        connect(host, port, "anonymous", "anonymous");
    }
    
    
    /**
     * Connects to an FTP server and logs in with the supplied username
     * and password.
     */
    public void connect(String host, int port, String user, String pass) throws IOException {
        if (socket != null) {
            throw new IOException("SimpleFTP is already connected. Disconnect first.");
        }
        
        socket = new Socket(host, port);
        reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        //TO RECEIVE THE SERIALIZED OBJECT
        input = new ObjectInputStream(socket.getInputStream());

        String response = readLine();
        if (!response.startsWith("220 ")) {
            throw new IOException("SimpleFTP received an unknown response when connecting to the FTP server: " + response);
        }
        
        sendLine("USER " + user);
        
        response = readLine();
        
        if (!response.startsWith("331 ")) {
        	System.out.println("2");
            throw new IOException("SimpleFTP received an unknown response after sending the user: " + response);
        }
        sendLine("PASS " + pass);
        
        response = readLine();
        if (!response.startsWith("230 ")) {
            throw new IOException("SimpleFTP was unable to log in with the supplied password: " + response);
        }
        // Now logged in.
    }
    
    
    /**
     * Disconnects from the FTP server.
     */
    public  void disconnect() throws IOException {
        try {
            sendLine("QUIT");
        }
        finally {
            socket = null;
        }
    }
    
    
    /**
     * Returns the working directory of the FTP server it is connected to.
     */
    public String pwd() throws IOException {
        sendLine("PWD");
        String dir = null;
        String response = readLine();
        if (response.startsWith("257 ")) {
        	System.out.println("PWD "+ response);
            int fsLastIndex = response.lastIndexOf(System.getProperty("file.separator"));
            if (fsLastIndex > 0) 
                dir = response.substring(fsLastIndex + 1);
             else 
            	 dir = response.substring(4);
        }
        return dir;
    }
    
    
    /**
     * Returns a list of files from a directory specified.
     */
    //WE RECEIVED THE OBJECT FOMR THE SERVER, THAT WAS SERIALIZED
    //TO GET THE INFORMATION ABOUT THE TREEFILE
    public String ls() throws IOException {
        sendLine("LIST ");
        String list = null;
        String response = readLine();
        if (response.startsWith("200 ")) 
                   list = response.split(" ")[1];
        try{
        	fileTree = (File) input.readObject();
        }
        catch(ClassNotFoundException e){
        	e.printStackTrace();
        }    
        return list;
    }
    

    /**
     * Changes the working directory (like cd). Returns true if successful.
     */   
    public boolean cwd(String dir) throws IOException {
        sendLine("CWD " + dir);
        String response = readLine();
        return (response.startsWith("250 "));
    }
    
    //THIS METHOD IS TO TELL THE SERVER TO GO UP ONE LEVEL IN THE FILE TREE
    public boolean cdup() throws IOException {
        sendLine("CDUP ");
        String response = readLine();
        return (response.startsWith("250 "));
    }
    
    
    /**
     * Sends a file to be stored on the FTP server.
     * Returns true if the file transfer was successful.
     * The file is sent in passive mode to avoid NAT or firewall problems
     * at the client end.
     */
    
    
    public boolean stor(File file) throws IOException {
        if (file.isDirectory()) {
            throw new IOException("SimpleFTP cannot upload a directory.");
        }
        
        String filename = file.getName();
        return stor(new FileInputStream(file), filename);
    }
    
    
    /**
     * Sends a file to be stored on the FTP server.
     * Returns true if the file transfer was successful.
     * The file is sent in passive mode to avoid NAT or firewall problems
     * at the client end.
     */
    @SuppressWarnings("resource")
	public  boolean stor(InputStream inputStream, String filename) throws IOException {

        BufferedInputStream input = new BufferedInputStream(inputStream);
        
        sendLine("PASV");
        String response = readLine();
        if (!response.startsWith("227 ")) {
            throw new IOException("SimpleFTP could not request passive mode: " + response);
        }
        
        String ip = null;
        int port = -1;
        int opening = response.indexOf('(');
        int closing = response.indexOf(')', opening + 1);
        if (closing > 0) {
            String dataLink = response.substring(opening + 1, closing);
            StringTokenizer tokenizer = new StringTokenizer(dataLink, ",");
            try {
                ip = tokenizer.nextToken() + "." + tokenizer.nextToken() + "." + tokenizer.nextToken() + "." + tokenizer.nextToken();
                port = Integer.parseInt(tokenizer.nextToken()) * 256 + Integer.parseInt(tokenizer.nextToken());
            }
            catch (Exception e) {
                throw new IOException("SimpleFTP received bad data link information: " + response);
            }
        }
        
        sendLine("STOR " + filename);
        
        Socket dataSocket = new Socket(ip, port);
        
        response = readLine();
        if (!response.startsWith("150 ")) {
            throw new IOException("SimpleFTP was not allowed to send the file: " + response);
            
        }
        
        BufferedOutputStream output = new BufferedOutputStream(dataSocket.getOutputStream());
        byte[] buffer = new byte[4096];
        int bytesRead = 0;
        while ((bytesRead = input.read(buffer)) != -1) {
            output.write(buffer, 0, bytesRead);
        }
        output.flush();
        output.close();
        input.close();
        dataSocket.close();
        response = readLine();
        return response.startsWith("226 ");
    }
    
    //Receives a file to be stored on the client's directory.

    public boolean retr(File file) throws IOException {
        if (file.isDirectory()) {
            throw new IOException("SimpleFTP cannot download a directory.");
        }
        
        String filename = file.getName();
        return retr(new FileOutputStream(file),filename);
    }
    
    
	@SuppressWarnings("resource")
	public boolean retr(OutputStream outputStream, String filename)throws IOException
	{
    	BufferedOutputStream output = new BufferedOutputStream(outputStream);
		sendLine("PASV");
		String response = readLine();
		
		if (!response.startsWith("227 ")) 
			throw new IOException("SimpleFTP could not request passive mode: " + response);

		String ip = null;
		int port = -1;
		int opening = response.indexOf('(');
		int closing = response.indexOf(')', opening + 1);
		if (closing > 0) 
		{
			String dataLink = response.substring(opening + 1, closing);
			StringTokenizer tokenizer = new StringTokenizer(dataLink, ",");
			try 
			{
				ip = tokenizer.nextToken() + "." + tokenizer.nextToken() + "."
				+ tokenizer.nextToken() + "." + tokenizer.nextToken();
				port = Integer.parseInt(tokenizer.nextToken()) * 256
				+ Integer.parseInt(tokenizer.nextToken());
			} 
			catch (Exception e) 
			{
				throw new IOException("Recibio informacion incorrecta para establecer el link.");
			}
		}
		sendLine("RETR " + filename);		
		
		Socket dataSocket = new Socket(ip, port);

	        response = readLine();
	        if (!response.startsWith("150 ")) {
	            throw new IOException("SimpleFTP was not allowed to download the file: " + response);
	        }
	        
			BufferedInputStream in = new BufferedInputStream(dataSocket.getInputStream());  
			byte[] buf = new byte[4096]; 
			int bytesRead = 0;
				while ((bytesRead = in.read(buf)) != -1) 
				{  
					output.write(buf, 0, bytesRead);
				}
			output.flush();  
			output.close();  
            in.close();
			dataSocket.close();
			dataSocket = null;
			response = readLine();
			return response.startsWith("226");
	}
    

    /**
     * Enter binary mode for sending binary files.
     */
    public boolean bin() throws IOException {
        sendLine("TYPE I");
        String response = readLine();
        return (response.startsWith("201 "));
    }
    
    
    /**
     * Enter ASCII mode for sending text files. This is usually the default
     * mode. Make sure you use binary mode if you are sending images or
     * other binary data, as ASCII mode is likely to corrupt them.
     */
    public boolean ascii() throws IOException {
        sendLine("TYPE A");
        String response = readLine();
        return (response.startsWith("202 "));
    }
    
    
    /**
     * Sends a raw command to the FTP server.
     */
    private void sendLine(String line) throws IOException {
        if (socket == null) {
            throw new IOException("SimpleFTP is not connected.");
        }
        try {
            writer.write(line + "\r\n");
            writer.flush();
            if (DEBUG) {
                System.out.println("> " + line);
            }
        }
        catch (IOException e) {
            socket = null;
            throw e;
        }
    }
    
    private String readLine() throws IOException {
        String line = reader.readLine();
        if (DEBUG) {
            System.out.println("< " + line);
        }
        return line;
    }
    
    public File getFileTree() {
    	return this.fileTree;
    }
    
    private Socket socket = null;
    private BufferedReader reader = null;
    private BufferedWriter writer = null;
    private ObjectInputStream input = null;
    private File fileTree;
    
    private static boolean DEBUG = false;
    

}